package com.tinysand.advert.advice;

import com.tinysand.advert.annotation.SkipResponseAdvice;
import com.tinysand.advert.support.CommonResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Objects;

/**
 * 对各服务接口中返回的数据进行统一拦截并处理成通用响应格式
 *
 * Created By TINY
 * Create Time 2020/2/14
 **/
@RestControllerAdvice
@Slf4j
@SuppressWarnings("unused")
public class ResponseDataAdvice implements ResponseBodyAdvice<Object> {

    /**
     * 如果目标方法的对应的类上或者方法上标注了{@link SkipResponseAdvice}注解，
     * 则不对其返回的数据做通用响应处理
     *
     * @param methodParameter 目标方法的包装对象
     * @param aClass 消息转换器
     * @return 是否执行通用响应处理
     */
    @Override
    public boolean supports(
            MethodParameter methodParameter,
            @NonNull Class<? extends HttpMessageConverter<?>> aClass) {
        // 类上标识
        if (methodParameter.getDeclaringClass().isAnnotationPresent(
                SkipResponseAdvice.class
        )) {
            return false;
        }

        // 方法上标识
        return !Objects.requireNonNull(methodParameter.getMethod())
                .isAnnotationPresent(SkipResponseAdvice.class);
    }

    /**
     * 在请求的数据返回到客户端之前对请求中返回的数据做统一响应处理{@link CommonResponse}
     *
     * @param o 需要处理的数据对象
     * @param methodParameter 目标方法的包装对象
     * @param mediaType 请求的数据媒体类型
     * @param aClass 消息转换器
     * @param request Http请求对象
     * @param response Http响应对象
     * @return 经过统一处理后的响应对象
     */
    @Override
    @SuppressWarnings("unchecked")
    public Object beforeBodyWrite(
            Object o, @NonNull MethodParameter methodParameter,
            @NonNull MediaType mediaType,
            @NonNull Class<? extends HttpMessageConverter<?>> aClass,
            @NonNull ServerHttpRequest request,
            @NonNull ServerHttpResponse response) {

        CommonResponse commonResponse = new CommonResponse(
                0, "请求成功");

        // 请求成功但响应数据为空，直接返回一个无 data 的统一响应
        if (null == o) {
            return commonResponse;
        } else if (o instanceof CommonResponse) { // 返回的是通用响应，无需包装
            return o;
        } else { // 需要包装
            commonResponse.setData(o);
        }

        log.info("对请求（{}）的方法（{}）返回的数据进行增强，mediaType（{}）",
                request.getURI(),
                Objects.requireNonNull(methodParameter.getMethod()).getName(),
                mediaType.toString());
        return commonResponse;
    }
}
